﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Vibz.TeamAssignment.Common;
using Vibz.TeamAssignment.Common.Entity;
using System.Security.Principal;
using System.Reflection;
using System.IO;
using System.Windows.Forms;
using Extensil.Common;
using Vibz.TeamAssignment.Sharepoint;

namespace Vibz.TeamAssignment
{
    internal enum ViewOption
    {
        Sequential,
        Hierarchy
    }
    internal class AssignmentClient
    {
        internal const string PersonalTask = "Personal Task";
        public delegate void ReportProgress(int progress, string message);
        IAssignmentManager _manager;
        ReportProgress _report;
        public bool IsConnected
        {
            get;
            set;
        }
        public AssignmentClient()
            : this(ReportStatus)
        {
        }
        public Form GetSettingsForm(BaseCategory category)
        {
            return _manager.GetSettingForm(category);
        }
        public AssignmentClient(ReportProgress report)
        {
            string aManager = System.Configuration.ConfigurationManager.AppSettings["AssignmentManager"];
            if(String.IsNullOrEmpty(aManager))
                throw new ManagerNotDefinedException();
            string[] managerClass = aManager.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

            string hpath = GetPluginHandler(managerClass[1].Trim());
            
            //Assembly asm = Load(hpath);
            //if(asm == null)
            //    throw new Exception(hpath + " not a valid assembly");
            //_manager = (IAssignmentManager)asm.CreateInstance(managerClass[0].Trim());

            // Open for debugging Assignment Manager
            _manager = new AssignmentManager();
            
            _report = report;
        }
        internal void Connect()
        {
            try
            {
                _manager.Connect();
                _manager.AddCustomTaskGroup(-1, PersonalTask);
                IsConnected = true;
            }
            catch(Exception exc)
            {
                IsConnected = false;
                throw exc;
            }
        }
        public static string GetPluginHandler(string name)
        {
            foreach(string extension in new string[] { "dll", "exe" })
            {
                if(File.Exists(name + "." + extension))
                    return name + "." + extension;
                if(File.Exists(Application.StartupPath + "/" + name + "." + extension))
                    return Application.StartupPath + "/" + name + "." + extension;
                string[] files = Directory.GetFiles(Application.StartupPath, name + "." + extension,
                    SearchOption.AllDirectories);
                if(files.Length > 0)
                    return files[0];
            }
            throw new Exception("No plugin handler found for '" + name + "' under application domain " + Application.StartupPath + ".");
        }
        public static Assembly Load(string assemblyPath)
        {

            Assembly asm = null;
            FileInfo fInfo = new FileInfo(assemblyPath);
            using(FileStream fs = new FileStream(fInfo.FullName, FileMode.Open, FileAccess.Read))
            {
                using(MemoryStream ms = new MemoryStream())
                {
                    byte[] b = new byte[4096];
                    while(fs.Read(b, 0, b.Length) > 0)
                    {
                        ms.Write(b, 0, b.Length);
                    }
                    asm = Assembly.Load(ms.ToArray());
                }
            }
            return asm;
        }
        public static void ReportStatus(int progress, string message)
        {
        }
        public int CreateUpdateTask(Task task, Task oldTask)
        {
            try
            {
                if(task.TaskID == -1)
                {
                    _report(50, "Creating task.");
                    task.CreatedBy = CurrentUser;
                    return _manager.CreateTask(task);
                }
                else
                {
                    _report(50, "Updating task.");
                    int id = _manager.UpdateTask(task);
                    if(task.DueDate.Date.CompareTo(oldTask.DueDate.Date) != 0)
                    {
                        Dictionary<string, RecipientType> recipients = new Dictionary<string, RecipientType>();
                        List<TaskAssignment> assList = GetAllAssignments(task.TaskID);
                        foreach(TaskAssignment ass in assList)
                        {
                            if(!recipients.ContainsKey(ass.AssignBy))
                                recipients.Add(ass.AssignBy, RecipientType.To);
                        }
                        TaskAssignment lAss = assList
                            .Where(a => a.ID == assList
                                .Select(aID => aID.ID)
                                .Max())
                            .ToList()[0];
                        if(!recipients.ContainsKey(lAss.AssignedTo))
                            recipients.Add(lAss.AssignedTo, RecipientType.To);
                        // Preserve rtf format
                        SendMail("Task update: " + task.Title,
                            "Dear Team Members,\r\n\r\nBelow task has been updated with given details."
                            + "\r\n\r\n\t" + task.Title
                            + "\r\n\tDescription: " + RevertRTF(task.TaskDescription)
                            + "\r\n\tDue Date: " + task.DueDate
                            + "\r\n\tEstimated hours: " + task.EstimatedDuration + " Hours"
                            ,
                            recipients);
                    }
                    return id;
                }
            }
            catch(Exception exc)
            {
                throw new Exception("Task could not be created. " + exc.Message);
            }
        }
        public int CreateUpdateAssignment(Task task, TaskAssignment tAss, TaskAssignment oldAss)
        {
            try
            {
                if(tAss.ID == -1)
                {
                    _report(50, "Assigning task.");
                    int id = _manager.CreateAssignment(tAss);
                    Dictionary<string, RecipientType> recipients = new Dictionary<string, RecipientType>();
                    recipients.Add(tAss.AssignedTo, RecipientType.To);
                    // Preserve rtf format
                    if(tAss.AssignedTo != tAss.AssignBy)
                        SendMail("Assignment: " + task.Title,
                            "Dear " + tAss.AssignedTo + "\r\n\r\n"
                            + ((oldAss != null && oldAss.AssignedTo != tAss.AssignedTo)
                                ? "A task has been assigned to you. "
                                : "Status of a task assigned to you has been changed.")
                            + "See the details below."
                            + "\r\n\r\n\t" + task.Title
                            + "\r\n\tDescription: " + RevertRTF(task.TaskDescription)
                            + "\r\n\tAssigned by: " + tAss.AssignBy
                            + "\r\n\tMessage from assigner: " + RevertRTF(tAss.AssignComment)
                            + "\r\n\tDue Date: " + task.DueDate
                            + "\r\n\tEstimated hours: " + task.EstimatedDuration + " Hours"
                            + "\r\n\tStatus: " + tAss.AssignStatus.ToString()
                            ,
                            recipients);
                    return id;
                }
                else
                {
                    _report(50, "Updating task assignment.");
                    return _manager.UpdateAssignment(tAss);
                }
            }
            catch(Exception exc)
            {
                throw new Exception("Task assignment could not be done. " + exc.Message);
            }
        }
        public int CreateTaskAssignment(Task task, Task oldTask, TaskAssignment oldAssignment)
        {
            return CreateTaskAssignment(task, oldTask, oldAssignment, CurrentUser, "", AssignStatus.Assigned.ToString());
        }
        public int CreateTaskAssignment(Task task, Task oldTask, TaskAssignment oldAssignment, string userId, string comment, string status)
        {
            AssignStatus assStatus = (AssignStatus)Enum.Parse(typeof(AssignStatus), status);
            task.Status = TaskStatus.Open;
            switch(assStatus)
            {
                case AssignStatus.Accepted:
                case AssignStatus.Assigned:
                case AssignStatus.Rejected:
                    task.Status = TaskStatus.Active;
                    break;
                case AssignStatus.Closed:
                    task.Status = TaskStatus.Close;
                    break;

            }
            int taskId = CreateUpdateTask(task, oldTask);
            if(taskId != -1)
            {
                TaskAssignment tAss = new TaskAssignment(taskId, userId, CurrentUser,
                       !GetAsignee().Contains(userId), DateTime.Now, comment, assStatus);

                if(oldAssignment != null && oldAssignment.AssignedTo == userId && oldAssignment.AssignStatus == assStatus)
                    tAss.ID = oldAssignment.ID;

                CreateUpdateAssignment(task, tAss, oldAssignment);

                return taskId;
            }
            return -1;
        }
        
        public Task GetTask(int taskID)
        {
            return _manager.GetTask(taskID);
        }
        public Task GetTask(string taskName)
        {
            return _manager.GetTask(taskName);
        }
        public List<TaskView> GetUsersTask(ViewOption view)
        {
            return GetUsersTask(CurrentUser, view);
        }
        public List<TaskView> GetUsersTask(string userID, ViewOption view)
        {
            List<TaskView> retVal = _manager.GetUsersTask(userID);
            switch(view)
            {
                case ViewOption.Sequential:
                    return retVal;
                case ViewOption.Hierarchy:
                    return retVal;
            }
            return retVal;
        }
        public List<TaskView> GetImmediateSubTask(int taskId)
        {
            return _manager.GetImmediateSubTask(taskId);
        }
        public List<Task> GetAllSubTask(int taskId)
        {
            return _manager.GetAllSubTask(taskId);
        }
        public TaskAssignment GetLatestAssignment(int taskID)
        {
            return _manager.GetLatestAssignment(taskID);
        }
        public List<TaskAssignment> GetAllAssignments(int taskID)
        {
            return _manager.GetAllAssignments(taskID);
        }
        public List<TaskGroup> GetUsersTaskGroup()
        {
            List<TaskGroup> retValue = _manager.GetUsersTaskGroup(CurrentUser);
            retValue.Insert(0, new TaskGroup(PersonalTask));
            return retValue;
        }
        public List<string> GetAllMembers()
        {
            return _manager.GetAllMembers();
        }
        public List<string> GetAsignee()
        {
            return _manager.GetAsignee(CurrentUser);
        }
        public List<string> GetNonAsignee()
        {
            return _manager.GetNonAsignee(CurrentUser);
        }
        public List<TaskMasterEntry> GetAllConfig()
        {
            return _manager.GetAllConfig();
        }
        public string GetConfig(string key)
        {
            return _manager.GetConfig(key);
        }
        public string GetGroupName(int groupId)
        {
            return _manager.GetGroupName(groupId);
        }
        public List<string> GetAssignStatusList()
        {
            return _manager.GetAssignStatusList();
        }
        public List<TaskGroup> GetDistinctTaskGroup(List<TaskView> tViewList)
        {
            if(tViewList == null)
                return new List<TaskGroup>();
            return tViewList.Select(tView => tView.Group).Distinct().ToList();
        }
        public Dictionary<int, string> GetDistinctAssignedDateTimeSpan(List<TaskView> tViewList)
        {
            Dictionary<int, string> retValue = new Dictionary<int, string>();

            if(tViewList == null)
                return retValue;
            foreach(int days in new int[] { 1, 7, 30, 180, 365 })
            {
                if(tViewList.Where(tView =>
                    tView.LatestAssignment.AssignRequestDate.CompareTo(DateTime.Today.AddDays(-1 * days)) > 0
                    && tView.LatestAssignment.AssignRequestDate.CompareTo(DateTime.Today) <= 0)
                    .Distinct().ToList().Count > 0)
                    retValue.Add(days, "Last " + days.ToString() + " days");
            }
            return retValue;
        }

        public Dictionary<int, string> GetDistinctDueDateTimeSpan(List<TaskView> tViewList)
        {
            Dictionary<int, string> retValue = new Dictionary<int, string>();
            if(tViewList == null)
                return retValue;
            foreach(int days in new int[] { 1, 7, 30, 180, 365 })
            {
                if(tViewList.Where(tView =>
                    tView.Task.DueDate.CompareTo(DateTime.Today.AddDays(days)) < 0)
                    .Distinct().ToList().Count > 0)
                    retValue.Add(days, "Next " + days.ToString() + " days");
            }
            return retValue;
        }
        public bool IsUserAdmin()
        {
            return _manager.IsUserAdmin(WindowsIdentity.GetCurrent().Name);
        }
        internal static string CurrentDomain
        {
            get
            {
                string s = WindowsIdentity.GetCurrent().Name;
                int stop = s.IndexOf("\\");
                return (stop > -1) ? s.Substring(0, stop + 1).ToLower() : null;
            }
        }
        internal static string CurrentUser
        {
            get
            {
                string s = WindowsIdentity.GetCurrent().Name;
                int stop = s.IndexOf("\\");
                return (stop > -1) ? s.Substring(stop + 1, s.Length - stop - 1).ToLower() : null;
            }
        }
        internal int QueryInterval
        {
            get
            {
                return _manager.QueryInterval;
            }
        }
        internal string RevertRTF(string rtfText)
        {
            if(!rtfText.Trim().StartsWith("{") || !rtfText.Trim().EndsWith("}"))
                return rtfText;
            try
            {
                System.Windows.Forms.RichTextBox rtBox = new System.Windows.Forms.RichTextBox();
                rtBox.Rtf = rtfText;
                string plainText = rtBox.Text;
                plainText = plainText.Replace("\r\n", ",");
                return plainText;
            }
            catch(Exception exc)
            {
                return rtfText;
            }
        }
        public int AddTeamMember(string userId, string managerId)
        {
            return _manager.AddTeamMember(userId, managerId);
        }
        public int UpdateTaskMaster(string key, string value)
        {
            return _manager.UpdateTaskMaster(key, value);
        }
        public int AddTaskGroup(string title)
        {
            return _manager.AddTaskGroup(title);
        }
        public int AddTaskGroupOwner(int groupId, string userId)
        {
            return _manager.AddTaskGroupOwner(groupId, userId);
        }
        public int DeleteTaskGroupOwner(int groupId, string userId)
        {
            return _manager.DeleteTaskGroupOwner(groupId, userId);
        }
        public List<TeamMember> GetAllTeamMember()
        {
            return _manager.GetAllTeamMember();
        }
        public List<TaskGroup> GetAllTaskGroup()
        {
            return _manager.GetAllTaskGroup();
        }
        public List<TaskGroupOwner> GetAllTaskGroupOwner()
        {
            return _manager.GetAllTaskGroupOwner();
        }
        public string[] GetViewColumnSequence()
        {
            string colSeq = System.Configuration.ConfigurationManager.AppSettings["TaskViewColumnSequence"];
            string[] colNames;

            if(!String.IsNullOrEmpty(colSeq))
                colNames = colSeq.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            else
                colNames = new string[] { "Title", "Description" };

            return colNames.Select(s => s.Trim()).ToArray();
        }
        public string[] GetViewColumn()
        {
            return Enum.GetNames(typeof(ViewColumns));
        }
        public void ClearCache()
        {
            _manager.ClearCache();
        }
        public void SendMail(string subject, string body, Dictionary<string, RecipientType> recipients)
        {
            SendMail(subject, body, recipients, false);
        }
        public void SendMail(string subject, string body, Dictionary<string, RecipientType> recipients, bool forceSend)
        {
            try
            {
                if(!forceSend)
                {
                    bool sendMail = false;
                    bool.TryParse(GetConfig(TaskMasterEntry.AutoMail), out sendMail);
                    if(!sendMail)
                        return;
                }

                string domain = GetConfig(TaskMasterEntry.Domain);
                if(string.IsNullOrEmpty(domain))
                {
                    AlertBox.ShowDialog("Domain is not defined.\r\n Domain can be set from Master Data tab of the Settings form.");
                    return;
                }
                string signature = "\r\n\r\nThank you,\r\nTask Manager (on behalf of " + CurrentUser + ")";
                body += signature;

                MailProcessor.SendMail(domain, subject, body, recipients);
            }
            catch(Exception exc)
            {
                ReportStatus(100, exc.Message);
            }
        }
        public void RequestTaskStatus(Task task)
        {
            Dictionary<string, RecipientType> recipients = new Dictionary<string, RecipientType>();
            List<TaskAssignment> assList = GetAllAssignments(task.TaskID);
            foreach(TaskAssignment ass in assList)
            {
                if(!recipients.ContainsKey(ass.AssignBy))
                    recipients.Add(ass.AssignBy, RecipientType.CC);
            }
            TaskAssignment lAss = assList
                .Where(a => a.ID == assList
                    .Select(aID => aID.ID)
                    .Max())
                .ToList()[0];
            if(!recipients.ContainsKey(lAss.AssignedTo))
                recipients.Add(lAss.AssignedTo, RecipientType.To);
            // Preserve rtf format
            SendMail("Status Request: " + task.Title,
                "Dear " + lAss.AssignedTo + "\r\n\r\nYou have been requested to provide status update for below task."
                            + "\r\n\r\n\t" + task.Title
                ,
                recipients, true);
        }
        public void SendInstantMessageToAllStakeholders(Task task)
        {
            List<string> users = new List<string>();
            List<TaskAssignment> assList = GetAllAssignments(task.TaskID);
            foreach(TaskAssignment ass in assList)
            {
                if(!users.Contains(ass.AssignBy))
                    users.Add(ass.AssignBy);
            }
            TaskAssignment lAss = assList
                .Where(a => a.ID == assList
                    .Select(aID => aID.ID)
                    .Max())
                .ToList()[0];
            if(!users.Contains(lAss.AssignedTo))
                users.Add(lAss.AssignedTo);

            string domain = GetConfig(TaskMasterEntry.Domain);
            if(string.IsNullOrEmpty(domain))
            {
                AlertBox.ShowDialog("Conversation can not be initiated. Domain not defined.\r\n Domain can be set from Master Data tab of the Settings form.");
                return;
            }

            ConversationProcessor.StartConversation(domain, users, task.Title);
        }
        public void SendInstantMessageToCurrentAssignedUser(Task task)
        {
            List<string> users = new List<string>();
            List<TaskAssignment> assList = GetAllAssignments(task.TaskID);
            TaskAssignment lAss = assList
                .Where(a => a.ID == assList
                    .Select(aID => aID.ID)
                    .Max())
                .ToList()[0];
            if(!users.Contains(lAss.AssignedTo))
                users.Add(lAss.AssignedTo);

            string domain = GetConfig(TaskMasterEntry.Domain);
            if(string.IsNullOrEmpty(domain))
            {
                AlertBox.ShowDialog("Conversation can not be initiated. Domain is not defined.\r\n Domain can be set from Master Data tab of the Settings form.");
                return;
            }

            ConversationProcessor.StartConversation(domain, users, task.Title);
        }
        public int GetTaskGroupID(string taskGroupName)
        {
            List<TaskGroup> tgList = GetAllTaskGroup();
            tgList = tgList.Where(t => t.Title.ToLower() == taskGroupName.ToLower()).ToList();
            if(tgList.Count == 0)
                return -1;
            else
                return tgList[0].ID;
        }
    }
}
